package pogvue.gui;

import pogvue.datamodel.AlignmentI;
import pogvue.datamodel.SequenceGroup;
import pogvue.datamodel.SequenceI;
import pogvue.gui.event.AlignViewportEvent;
import pogvue.gui.event.AlignViewportListener;
import pogvue.gui.event.StatusEvent;
import pogvue.gui.menus.JalAction;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public final class SeqPanel extends JPanel implements AdjustmentListener,
						MouseListener,
						MouseMotionListener,
						AlignViewportListener
{

  private Controller controller;
  public final AlignmentI        align;
  public    SeqCanvas         seqCanvas;
  public    ScalePanel        scalePanel;

  private JScrollBar hscroll;
  private JScrollBar vscroll;

  private int oldoffx;
  private int oldoffy;

  private int maxoffx = 0;
  private int maxoffy = 0;

  private int startres;
  private int lastres;
  private int endres;


  private int startseq;
  private int padseq;

  public final boolean fastDraw = true;
  private boolean editFlag;
  private boolean showScale = true;

    private boolean mouseDragged = false;

  private final AlignViewport av;

  public SeqPanel(AlignViewport av,Controller c) {
    this.av         = av;
    this.align      = av.getAlignment();

    setController(c);
    c.addListener(this);
    componentInit();

    addMouseMotionListener(this);
    addMouseListener(this);

    
  }

  private void componentInit() {
      seqCanvas  = new SeqCanvas(av,controller);

      scalePanel = new ScalePanel(av,controller);
      
      hscroll = new JScrollBar(Scrollbar.HORIZONTAL);
      vscroll = new JScrollBar(Scrollbar.VERTICAL);
      
      hscroll.addAdjustmentListener(this);
      vscroll.addAdjustmentListener(this);
      
      hscroll.addMouseMotionListener(this);
      hscroll.addMouseListener(this);

      setLayout(new BorderLayout());
      
      if (showScale) {
	  JPanel sh  = new JPanel();
	  JPanel sh2 = new JPanel();

	  sh.setLayout(new BorderLayout());
	  sh.add("Center",scalePanel);
	  sh.add("West",new SeqFillPanel(SeqFillPanel.LEFT,av,controller));

	  sh2.setLayout(new BorderLayout());
	  sh2.add("Center",sh);
	  //sh2.add("East",new ScrollFillPanel(vscroll));
	  
	  add("North",sh2);
      }
      
    JPanel sch = new JPanel();
    sch.setLayout(new BorderLayout());

    sch.add("Center",seqCanvas);

    sch.add("West", new SeqFillPanel(SeqFillPanel.LEFT,av,controller));
    sch.add("North",new SeqFillPanel(SeqFillPanel.TOP,av,controller));

    Panel hh = new Panel();
    hh.setLayout(new BorderLayout());
    hh.add("Center",hscroll);
    hh.add("West",new SeqFillPanel(SeqFillPanel.LEFT,av,controller));

    Panel hh2 = new Panel();
    hh2.setLayout(new BorderLayout());
    hh2.add("Center",hh);
    hh2.add("East",new ScrollFillPanel(vscroll,SystemColor.scrollbar));

    Panel vh = new Panel();
    vh.setLayout(new BorderLayout());
    vh.add("Center",vscroll);
    vh.add("North",new SeqFillPanel(SeqFillPanel.TOP,av,controller));

    add("Center",sch);
    add("East",vh);
    add("South",hh2);

    setScrollValues(0,0);

    seqCanvas.addMouseListener(this);
    seqCanvas.addMouseMotionListener(this);

    System.out.println("SeqPanel initialized");
  }



  private void setController(Controller c) {
    controller = c;
    controller.addListener(this);
  }
 
  public Controller getController() {
    return controller;
  }

  private void setScrollValues(int offx, int offy) {

    int width;
    int height;

    //Brings up error in netscape
    if (seqCanvas.getSize().width > 0) {
      width  = seqCanvas.getSize().width;
      height = seqCanvas.getSize().height;
    } else {
      width  = seqCanvas.getPreferredSize().width;
      height = seqCanvas.getPreferredSize().height;
    }

    //Make sure the maxima are right
    if (maxoffx != (align.getWidth())) {
      maxoffx = (align.getWidth());
    }
    if (maxoffy != (align.getHeight())) {
      maxoffy = (align.getHeight());
    }

    //The extra 1 is to make sure all the last character gets printed
    // SMJS Removed the extra one

    if (av.getWrapAlignment() == false) {
	showScale = false;

	hscroll.setValues(offx, (int)(width/av.getCharWidth()),0,maxoffx);
	vscroll.setValues(offy,height/av.getCharHeight(),0,maxoffy);

	int hpageinc = (int)(0.3 * (av.getEndRes() - av.getStartRes()));
	int vpageinc = (int)(0.3 * (av.getEndSeq() - av.getStartSeq()));

	hscroll.setUnitIncrement(hpageinc/10 + 1);
	vscroll.setUnitIncrement(1);
	
	
	if (hpageinc < 1) {
	    hpageinc = 1;
	}
	if (vpageinc < 1) {
	    vpageinc = 1;
	}

	hscroll.setBlockIncrement(hpageinc);
	vscroll.setBlockIncrement(vpageinc);
    } else {
	showScale = true;
	maxoffy = ((align.getHeight()/seqCanvas.getChunkWidth()) + 2)*(align.getHeight()+1);
	maxoffx = ((align.getWidth()/seqCanvas.getChunkWidth()) + 2)*(align.getWidth()+1);
	vscroll.setValues(offy,height/av.getCharHeight(),0,maxoffy);
	hscroll.setValues(offx,(int)(width/av.getCharWidth()),0,maxoffx);
	
	hscroll.setUnitIncrement(1);
	vscroll.setUnitIncrement(1);
    }
  }

  public int getScaleHeight() {
    if (showScale) {

      return scalePanel.getSize().height;
    } else {
      return 0;
    }

  }
  public void adjustmentValueChanged(AdjustmentEvent evt) {

    if (evt.getSource() == hscroll) {

      int offx = hscroll.getValue();

      if (offx != oldoffx) {
        av.setStartRes(offx);
        av.setEndRes((int)(offx + (seqCanvas.size().width/av.getCharWidth()-1)));

	if (mouseDragged) {
	    //System.out.println("Firing event");
	    controller.handleAlignViewportEvent(new AlignViewportEvent(this,av,AlignViewportEvent.SHOW));
	    mouseDragged = false;
	}
	
        hscroll.validate();
      }
      oldoffx = offx;
    }

    if (evt.getSource() == vscroll) {
      int offy = vscroll.getValue();
      if (oldoffy != offy) {
        av.setStartSeq(offy);
        av.setEndSeq(offy + seqCanvas.getSize().height/av.getCharHeight());
        controller.handleAlignViewportEvent(new AlignViewportEvent(this,av,AlignViewportEvent.LIMITS));
      }
      oldoffy = offy;
    }
  }
  public class EditSequenceAction extends JalAction {
    public EditSequenceAction(String name) {
      super(name);
      addRequiredArg("sequence");
      addRequiredArg("position");
      addRequiredArg("length");
    }
    public void applyAction(ActionEvent evt) {
      if (evt.getSource() == this) {
      } else {
      }
    }
  }
  public void mouseEntered(MouseEvent evt) { }
  public void mouseExited(MouseEvent evt) { }
  public void mouseClicked(MouseEvent evt) { }

  public void mouseReleased(MouseEvent evt) {

      if (evt.getSource() == hscroll) {
	  //System.out.println("Dragged in down");
	  mouseDragged = true;
	    
	  return;
      }
    int x = evt.getX();
    int y = evt.getY();

    if (editFlag) {
      System.out.println("End edit");
    }

      int res = (int)(x/av.getCharWidth() + av.getStartRes());
      int seq = av.getIndex(y);

      char resstr = align.getSequenceAt(seq).getSequence().charAt(res);

      endres = res;
      
      int pos = align.getSequenceAt(seq).findPosition(res);
      

      // Minipogness - we have the centre so we need to calculate the start and ends

      if (av.getMinipog() != null) {
	  AlignViewport av2 = av.getMinipog().getAlignViewport();
	  
	  int width = av2.getEndRes() - av2.getStartRes() + 1;

	  double charwidth = av2.getCharWidth();

	  int startres = res - width/2;
	  int endres   = startres + width;

	  av2.setStartRes(startres);
	  av2.setEndRes(endres);

	  av.getMinipog().getController().handleAlignViewportEvent(new AlignViewportEvent(this,av2,AlignViewportEvent.HSCROLL));
      }
      // This is to detect edits - we're at the end of an edit if mouse is up
      editFlag = false;
      startseq = -1;
      startres = -1;
      lastres  = -1;
  }

  public void mousePressed(MouseEvent evt) {

      if (evt.getSource() == hscroll) {
	  //System.out.println("Dragged in down");
	  mouseDragged = true;
	    
	  return;
      }
    int seq;
    int res;
    int pos;

    int x = evt.getX();
    int y = evt.getY();

    res = (int)(x/av.getCharWidth() + av.getStartRes());
    seq = (y)/av.getCharHeight() + av.getStartSeq();
    
    if (seq < align.getHeight() && res < align.getWidth() && res >= 0) {
      char resstr = align.getSequenceAt(seq).getSequence().charAt(res);
      
      // Find the residue's position in the sequence (res is the position
      // in the alignment
      
      pos = align.getSequenceAt(seq).findPosition(res);
      startseq = seq;
      
      if (startseq == (align.getHeight()-1)) {
        padseq = 1;
      } else {
        padseq = 1;
      }

      startres = res;
      lastres = res;
      
      controller.handleStatusEvent(new StatusEvent(this,"Sequence ID : " + align.getSequenceAt(seq).getName() +    
                                   " (" + seq + ") Residue = "     +  resstr + " (" + pos + ") ",StatusEvent.INFO));
    } else {
      startseq = -1;
      startres = -1;
      lastres = -1;
    }

  }

    public void mouseMoved(MouseEvent evt) { }
    
    public void mouseDragged(MouseEvent evt) {
	
	if (evt.getSource() == hscroll) {
	    mouseDragged = true;
	}
    }

    public void mouseDown(MouseEvent evt) {
	System.out.println("Got event");

	if (evt.getSource() == hscroll) {
	    //System.out.println("Dragged in down");
	    mouseDragged = true;
	}
    }

  public void old_mouseDragged(MouseEvent evt) {
    // If we're dragging we're editing
    editFlag = true;
  
    int x = evt.getX();
    int y = evt.getY();

    int res = (int)(x/av.getCharWidth() + av.getStartRes());
    if (res < 0) {res = 0;}
    
    if (res  != lastres) {
      if (startseq != -1) {
	char resstr = align.getSequenceAt(startseq).getSequence().charAt(res);
	
	// Group editing
	if (av.getGroupEdit()) {
	  int start = lastres;
	  SequenceGroup sg = align.findGroup(startseq);
	  
	  if (res < align.getWidth() && res < lastres) { 
	    boolean flag = false;
	    for (int i= 0 ; i < sg.getSize(); i++) {
	      SequenceI s = sg.getSequenceAt(i);
	      for (int j=lastres-1; j >= res; j--) {
		if (!flag) {
		  if (!s.getSequence().substring(j,j+1).equals("-") && !s.getSequence().substring(j,j+1).equals(".") &&
		      !s.getSequence().substring(j,j+1).equals(" ")) {
		    res = j+1;
		    System.out.print("\07");
		    System.out.flush();
		    flag = true;
		  }
		}
	      }
	    }
	  }
	  
	  for (int i= 0 ; i < sg.getSize(); i++) {
	    SequenceI s = sg.getSequenceAt(i);
	    boolean found = false;
	    int sno = -1;
	    for (int k = 0; k < align.getHeight(); k++) {
	      if (align.getSequenceAt(k) == s) {
		found = true;
		sno = k;
		break;
	      }
	    }
	    if (found && sno != -1) {
	      if (res < align.getWidth() && res > lastres) {
		for (int j = lastres; j < res; j++) {
		    insertChar(j,sno);
		}
		int index = align.findIndex(s);
		if (index != -1) {
		  drawChars(index,index+1,lastres,align.getWidth());
		}
		
	      } else if (res < align.getWidth() && res < lastres) { 
		for (int j = res; j < lastres; j++) { 
		  deleteChar(j,res,sno);
		  startres = res;
		}
		int index = align.findIndex(s);
		if (index != -1) {
		  drawChars(index,index+1,res,align.getWidth());
		}
	      }
	    }
	    
	  }
	  lastres = res;
	} else {

	  if (res < align.getWidth() && res > lastres) {

	    for (int j = lastres; j < res; j++) {
	      insertChar(j,startseq);
	    }
	    drawChars(startseq,startseq+1,lastres,align.getWidth());
	    
	  } else if (res < align.getWidth() && res < lastres) { 
	    for (int j = res; j < lastres; j++) { 
	      deleteChar(j,res,startseq);
	      startres = res;
	    }
	    drawChars(startseq,startseq+1,res,align.getWidth());
	  }
	}
      }
      lastres = res;
    }
  }

  public void reshape(int x, int y, int width, int height) {
    super.reshape(x,y,width,height);
    setScrollValues(av.getStartRes(),av.getStartSeq());
  }
  public boolean handleAlignViewportEvent(AlignViewportEvent e) {
    System.out.println("Handling alignviewport event");
    setScrollValues(av.getStartRes(),av.getStartSeq());
    return true;
  }
  private void drawChars(int seqstart, int seqend, int start, int end) {
    seqCanvas.drawPanel(seqCanvas.getGraphics(),start,end,seqstart,seqend,av.getStartRes(),av.getStartSeq(),0);
    seqCanvas.drawPanel(seqCanvas.gg,           start,end,seqstart,seqend,av.getStartRes(),av.getStartSeq(),0);
  }
 
  private void insertChar(int j, int seq) {

    align.getSequenceAt(seq).insertCharAt(j,av.getGapCharacter().charAt(0));
    align.getWidth();

    //controller.handleEditEvent(new EditEvent(this,new SequenceEdit(align.getSequenceAt(seq),j,SequenceEdit.ADD)));

    setScrollValues(av.getStartRes(),av.getStartSeq());
 
  }
 
  private void deleteChar(int j, int res, int sno) {
 
    if (align.getSequenceAt(sno).getSequence().substring(j,j+1).equals(".") ||
        align.getSequenceAt(sno).getSequence().substring(j,j+1).equals("-") ||
        align.getSequenceAt(sno).getSequence().substring(j,j+1).equals(" ") ) {
 
      align.getSequenceAt(sno).deleteCharAt(j);

    }

    align.getWidth();
    //controller.handleEditEvent(new EditEvent(this,new SequenceEdit(align.getSequenceAt(sno),j,SequenceEdit.DELETE)));

    setScrollValues(av.getStartRes(),av.getStartSeq());
  }

  public JScrollBar getHScroller() {
    return hscroll;
  }
}




